package com.ruoyi.recycleshop.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.recycle.constant.OrderInfoConstant;
import com.ruoyi.recycle.domain.*;
import com.ruoyi.recycle.dto.AttractDataQueryDTO;
import com.ruoyi.recycle.dto.MerchantBillRecordQueryDTO;
import com.ruoyi.recycle.dto.NativePayDTO;
import com.ruoyi.recycle.exception.BusinessException;
import com.ruoyi.recycle.mapper.BillRecordMapper;
import com.ruoyi.recycle.mapper.ClientUserManagementMapper;
import com.ruoyi.recycle.properties.Wxproperties;
import com.ruoyi.recycle.service.*;
import com.ruoyi.recycle.service.impl.WechatPaymentService;
import com.ruoyi.recycle.utils.OrderNoGenerateUtil;
import com.ruoyi.recycle.utils.OssUpload;
import com.ruoyi.recycle.vo.AttractDataVO;
import com.ruoyi.recycle.vo.BillRecordVO;
import com.ruoyi.recycle.vo.CouponInfoVO;
import com.ruoyi.recycle.vo.MultiExchangeVO;
import com.ruoyi.recycleshop.domain.ShopExchangeRecord;
import com.ruoyi.recycleshop.dto.ConvertToMultipartFile;
import com.ruoyi.recycleshop.dto.ShopExchangeQueryDTO;
import com.ruoyi.recycleshop.event.CreateCouponEvent;
import com.ruoyi.recycleshop.event.DeleteCouponEvent;
import com.ruoyi.recycleshop.event.ModifyCouponEvent;
import com.ruoyi.recycleshop.mapper.ShopExchangeRecordMapper;
import com.ruoyi.recycleshop.service.CashRegisterService;
import com.ruoyi.recycleshop.utils.QRCodeUtil;
import com.ruoyi.recycleshop.vo.ExchangeStatisticsVO;
import com.ruoyi.recycleshop.vo.PayInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


@Service
@Slf4j
public class CashRegisterServiceImpl implements CashRegisterService {
    @Autowired
    private CouponService couponService;
    @Autowired
    private OrderNoGenerateUtil orderNoGenerateUtil;
    @Autowired
    private IClientUserManagementService userManagementService;
    @Autowired
    private ShopExchangeRecordMapper exchangeRecordMapper;
    @Autowired
    private Wxproperties wxproperties;
    @Autowired
    private IIntegralConfigService integralConfigService;
    @Autowired
    private BillRecordService billRecordService;
    @Autowired
    private IShopManagementService shopManagementService;
    @Autowired
    private ExchangeRecordService exchangeRecordService;
    @Autowired
    private IFinanceManagementService financeManagementService;
    @Autowired
    private IClientUserManagementService clientUserManagementService;
    @Autowired
    private IRecycleOrderService recycleOrderService;
    @Autowired
    private CouponStockService stockService;
    @Autowired
    private ClientCouponService clientCouponService;
    @Autowired
    private WechatPaymentService wechatPaymentService;
    @Autowired
    private IHousekeepingOrderService housekeepingOrderService;


    @Resource(name = "couponThreadPool")
    private ThreadPoolTaskExecutor taskExecutor;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean createCoupon(Coupon coupon) throws Exception {
        couponService.save(coupon.setStatus("未生效"));
        final Long id = coupon.getId();
        try {
            List<CouponStock> stockList = new ArrayList<>();
            List<Future<CouponStock>> couponFutureTasks = new ArrayList<>();
            for (int i = 0; i < coupon.getNumber(); i++) {
                couponFutureTasks.add(createCouponStock(id));
                Long memory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
                log.info("当前程序占用运行内存{}M", memory);
            }
            for (int a = 0; a < couponFutureTasks.size(); a++) {
                final Future<CouponStock> stockFuture = couponFutureTasks.get(a);
                while (!stockFuture.isDone()) {
                }
                stockList.add(stockFuture.get());
            }
            stockService.saveBatch(stockList);
            SpringUtil.publishEvent(new CreateCouponEvent(coupon));
            return true;
        } catch (Exception e) {
            log.error("创建优惠券失败,原因:{}", e.getMessage());
            throw e;
        }
    }

    @Async
    public Future<CouponStock> createCouponStock(Long couponId) {
        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            return taskExecutor.submit(() -> {
                final String couponNo = orderNoGenerateUtil.getIncr(OrderInfoConstant.COUPON_NO);
                final BufferedImage image = QRCodeUtil.createImage(StrUtil.format("https://bluestarhuman.com/recycle/ruoyi-recycle-shop/cashRegister/getCouponDetail?couponNo={}", couponNo));
                ImageIO.write(image, "jpg", outputStream);
                final byte[] imageBytes = outputStream.toByteArray();
                final MultipartFile file = new ConvertToMultipartFile(imageBytes, couponNo, "", "jpg", imageBytes.length);
                final String uploadFile = OssUpload.uploadFile(file, ".jpg");
                outputStream.reset();
                return new CouponStock()
                        .setCouponId(couponId)
                        .setCouponNo(couponNo)
                        .setQrCode(uploadFile)
                        .setStatus("待领取");
            });
        } catch (Exception e) {
            log.error("批量创建优惠券失败,原因:{}", e.getMessage());
            return null;
        } finally {
            IoUtil.close(outputStream);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean modifyCoupon(Coupon coupon) {
        final Coupon data = couponService.getById(coupon.getId());
        final List<CouponStock> stockList = stockService.list(Wrappers.lambdaQuery(new CouponStock())
                .eq(CouponStock::getCouponId, coupon.getId()));
        final List<CouponStock> drawList = stockList.stream()
                .filter(s -> StrUtil.equals(s.getStatus(), "已领取"))
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(drawList)) {
            throw new BusinessException("该优惠券已有客户领取,不可更改");
        }
        try {
            couponService.updateById(coupon);
            if (data.getEffectStart() != coupon.getEffectStart() || data.getEffectEnd() != coupon.getEffectEnd()) {
                SpringUtil.publishEvent(new ModifyCouponEvent(coupon));
            }
            return true;
        } catch (Exception e) {
            log.info("修改优惠券失败,原因:{}", e.getMessage());
            return false;
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteCoupon(Long couponId) {
        try {
            final Coupon coupon = couponService.getById(couponId);
            if (StrUtil.equals(coupon.getStatus(), "已生效")) {
                final List<CouponStock> drewList = stockService.list(Wrappers.lambdaQuery(new CouponStock())
                        .eq(CouponStock::getCouponId, couponId).
                                eq(CouponStock::getStatus, "已领取"));
                if (CollUtil.isNotEmpty(drewList)) {
                    throw new BusinessException("该优惠券已有客户领取,不可随意删除");
                }
            }
            couponService.removeById(couponId);
            stockService.remove(Wrappers.lambdaQuery(new CouponStock()).eq(CouponStock::getCouponId, couponId));
            SpringUtil.publishEvent(new DeleteCouponEvent(String.valueOf(couponId)));
            return true;
        } catch (Exception e) {
            log.info("删除优惠券失败,原因:{}", e.getMessage());
            return false;
        }

    }


    @Override
    public CouponInfoVO getCouponDetail(String couponNo) {
        final CouponStock stockInfo = stockService.getById(couponNo);
        final Long couponId = stockInfo.getCouponId();
        final Coupon coupon = couponService.getById(couponId);
        return new CouponInfoVO()
                .setConditionDetail(coupon.getConditionDetail())
                .setEffectEnd(coupon.getEffectEnd())
                .setEffectStart(coupon.getEffectStart())
                .setCouponName(coupon.getCouponName())
                .setCouponId(stockInfo.getCouponId())
                .setCouponNo(couponNo)
                .setQrCode(stockInfo.getQrCode())
                .setShopName(coupon.getShopName())
                .setDenomination(coupon.getDenomination())
                .setStatus(stockInfo.getStatus())
                .setType(coupon.getType());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String pay(NativePayDTO payDTO) {
        final String couponNo = payDTO.getCouponNo();
        final CouponStock couponStock = stockService.getById(couponNo);
        final ClientCoupon record = clientCouponService.getOne(Wrappers.lambdaQuery(new ClientCoupon()).eq(ClientCoupon::getCouponNo, payDTO.getCouponNo()));
        final Coupon coupon = couponService.getById(couponStock.getCouponId());
        final IntegralConfig config = integralConfigService.getOne(Wrappers.lambdaQuery(new IntegralConfig()).eq(IntegralConfig::getType, "1"));
        final String shopChargeOrderNo = orderNoGenerateUtil.getIncr(OrderInfoConstant.SHOP_CHARGE_NO);
        final ClientUserManagement user = userManagementService.getById(record.getClientId());
        final ShopManagement shop = shopManagementService.getById(payDTO.getShopId());
        final BigDecimal amount = Objects.isNull(payDTO.getAmount()) ? BigDecimal.ZERO : payDTO.getAmount();
        final int compare = amount.compareTo(BigDecimal.ZERO);
        String codeUrl = "";
        final ShopExchangeRecord exchangeRecord = new ShopExchangeRecord()
                .setAmount(amount)
                .setOriginAmount(payDTO.getOriginAmount())
                .setCouponNo(couponNo)
                .setDiscount(coupon.getDenomination())
                .setCouponDetail(coupon.getConditionDetail())
                .setUserId(user.getId())
                .setOrderNo(shopChargeOrderNo)
                .setShopId(payDTO.getShopId())
                .setShopName(shop.getShopName())
                .setBlueCoinPay(payDTO.getBlueCoin() == null ? 0L : payDTO.getBlueCoin());
        NativePayDTO nativePayDTO = payDTO.setAppId(wxproperties.getShopAppId())
                .setNotifyUrl("https://bluestarhuman.com/recycle/ruoyi-recycle-shop/cashRegister/payCallBack")
                .setOrderNo(shopChargeOrderNo);
        BigDecimal totalBlueCoins = amount.multiply(BigDecimal.valueOf(config.getIntegral()), MathContext.UNLIMITED);
        if (Objects.nonNull(payDTO.getBlueCoin()) && compare > 0) {
            exchangeRecordMapper.insert(exchangeRecord.setPayType("积分支付+微信支付").setIncomeType("用户下单").setStatus("支付中"));
            codeUrl = wechatPaymentService.nativePay(nativePayDTO);
            Assert.isTrue(StrUtil.isNotEmpty(codeUrl), () -> new BusinessException("生成支付码失败"));
            totalBlueCoins = BigDecimal.valueOf(payDTO.getBlueCoin()).add(totalBlueCoins);
            userManagementService.updateById(user.setIntegral(user.getIntegral() - payDTO.getBlueCoin()));
        } else {
            exchangeRecordMapper.insert(exchangeRecord.setPayType("微信支付").setIncomeType("用户下单").setStatus("支付中"));
            codeUrl = wechatPaymentService.nativePay(nativePayDTO);
            Assert.isTrue(StrUtil.isNotEmpty(codeUrl), () -> new BusinessException("生成支付码失败"));
        }
        billRecordService.save(new BillRecord().setAccount(shop.getShopNumber())
                .setIncome(totalBlueCoins)
                .setOrderNo(shopChargeOrderNo)
                .setType(1).setUserType(3));
        clientCouponService.updateById(record.setStatus("已使用"));
        shopManagementService.updateById(shop.setIntegral(shop.getIntegral() + totalBlueCoins.longValue()));
        exchangeRecordService.save(new ExchangeRecord()
                .setBlueCoin(payDTO.getBlueCoin()).setOrderNo(shopChargeOrderNo)
                .setType("商店客户充值")
                .setSource("引流门店")
                .setAmount(payDTO.getAmount())
                .setAccount(String.valueOf(user.getId()))
                .setExchangeStatus("支付中"));
        return codeUrl;
    }

    @Override
    public ExchangeStatisticsVO exchangeStatistics(Long shopId) {
        final ExchangeStatisticsVO statisticsVO = new ExchangeStatisticsVO();
        final Date date = new Date();
        final Date dayBefore = DateUtil.offsetDay(date, -1).toJdkDate();
        final Date month = DateUtil.beginOfMonth(date).toJdkDate();
        final Date lastMonth = DateUtil.offsetMonth(date, -1).toJdkDate();
        final BigDecimal zero = BigDecimal.ZERO;
        final BigDecimal dailyIncome = exchangeRecordMapper.sumAmountByTime(shopId, DateUtil.formatDate(date));
        final BigDecimal dailyBeforeIncome = exchangeRecordMapper.sumAmountByTime(shopId, DateUtil.formatDate(dayBefore));
        final BigDecimal monthlyIncome = exchangeRecordMapper.sumAmountByTime(shopId, DateUtil.format(month, "yyyy-MM"));
        final BigDecimal lastMonthlyIncome = exchangeRecordMapper.sumAmountByTime(shopId, DateUtil.format(lastMonth, "yyyy-MM"));
        return statisticsVO.setDayStatistics(Objects.isNull(dailyIncome) ? zero : dailyIncome)
                .setDayBeforeStatistics(Objects.isNull(dailyBeforeIncome) ? zero : dailyBeforeIncome)
                .setMonthStatistics(Objects.isNull(monthlyIncome) ? zero : monthlyIncome)
                .setMonthBeforeStatistics(Objects.isNull(lastMonthlyIncome) ? zero : lastMonthlyIncome);
    }

    @Override
    public List<ShopExchangeRecord> exchangeList(ShopExchangeQueryDTO queryDTO) {
        final LambdaQueryWrapper<ShopExchangeRecord> queryWrapper = Wrappers.lambdaQuery(new ShopExchangeRecord())
                .eq(ShopExchangeRecord::getShopId, queryDTO.getShopId());
        if (Objects.nonNull(queryDTO.getSearchBegin())) {
            queryWrapper.ge(ShopExchangeRecord::getCreateTime, queryDTO.getSearchBegin());
        }
        if (Objects.nonNull(queryDTO.getSearchEnd())) {
            queryWrapper.le(ShopExchangeRecord::getCreateTime, queryDTO.getSearchEnd());
        }

        return exchangeRecordMapper.selectList(queryWrapper);

    }

    @Override
    public BillRecordVO billRecord(MerchantBillRecordQueryDTO queryDTO) {
        final LambdaQueryWrapper<BillRecord> queryWrapper = Wrappers.lambdaQuery(new BillRecord())
                .eq(BillRecord::getAccount, queryDTO.getAccount());
        if (queryDTO.getSearchTimeBegin() != null) {
            queryWrapper.ge(BillRecord::getCreateTime, queryDTO.getSearchTimeBegin());
        }
        if (queryDTO.getSearchTimeEnd() != null) {
            queryWrapper.le(BillRecord::getCreateTime, queryDTO.getSearchTimeEnd());
        }
        final List<BillRecord> records = billRecordService.list(queryWrapper);
        final List<BillRecord> incomeRecords = records.stream()
                .filter(i -> i.getType() == 0 || i.getType() == 1)
                .collect(Collectors.toList());
        final Double incomeAmount = incomeRecords.stream().map(BillRecord::getIncome).mapToDouble(BigDecimal::doubleValue).sum();
        final List<BillRecord> outputRecords = records.stream()
                .filter(o -> o.getType() == 3)
                .collect(Collectors.toList());
        final Double outputAmount = outputRecords.stream()
                .map(BillRecord::getOutput).mapToDouble(BigDecimal::doubleValue).sum();
        return new BillRecordVO()
                .setIncomeRecords(incomeRecords)
                .setOutputRecords(outputRecords)
                .setOutputAmount(outputAmount)
                .setIncomeAmount(incomeAmount);
    }

    @Override
    public Map<String, List<MultiExchangeVO>> exchangeRecord(Long shopId) {
        Map<String, List<MultiExchangeVO>> multiExchangeMap = new HashMap<>();
        multiExchangeMap.put("提现记录", null);
        multiExchangeMap.put("收入记录", null);
        final ShopManagement shop = shopManagementService.getById(shopId);
        final List<ExchangeRecord> recordList = exchangeRecordService.list(Wrappers.lambdaQuery(new ExchangeRecord())
                .eq(ExchangeRecord::getAccount, shop.getShopNumber()));
        final List<ShopExchangeRecord> exchangeRecords = exchangeRecordMapper.selectList(Wrappers.lambdaQuery(new ShopExchangeRecord())
                .eq(ShopExchangeRecord::getShopId, shopId));
        if (CollUtil.isNotEmpty(recordList)) {
            final List<ExchangeRecord> withdrawRecords = recordList.stream().filter(r -> StrUtil.equals(r.getType(), "提现")).collect(Collectors.toList());
            List<MultiExchangeVO> withdraws = new ArrayList<>();
            if (CollUtil.isNotEmpty(withdrawRecords)) {
                withdrawRecords.forEach(wr -> {
                    withdraws.add(new MultiExchangeVO()
                            .setWithdrawStatus(wr.getExchangeStatus())
                            .setAmount(wr.getAmount())
                            .setExchangeDate(wr.getCreateTime()));
                });
                multiExchangeMap.put("提现记录", withdraws);
            }
        }
        if (CollUtil.isNotEmpty(exchangeRecords)) {
            List<MultiExchangeVO> integrals = new ArrayList<>();
            exchangeRecords.forEach(e -> {
                integrals.add(new MultiExchangeVO().setAmount(e.getAmount()).setIncomeType(e.getIncomeType()).setExchangeDate(e.getCreateTime()));
            });
            multiExchangeMap.put("收入记录", integrals);
        }


        return multiExchangeMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean withdraw(String account, Integer withdrawAmount) {
        final ShopManagement shop = shopManagementService.getOne(Wrappers.lambdaQuery(new ShopManagement())
                .eq(ShopManagement::getShopNumber, account));
        Assert.isFalse(withdrawAmount > shop.getIntegral(), () -> new BusinessException("提现积分大于当前账户积分,请输入正确的提现积分"));
        shopManagementService.updateById(shop.setIntegral(shop.getIntegral() - withdrawAmount));
        final String withdrawOrderNo = orderNoGenerateUtil.getIncr(OrderInfoConstant.WITHDRAW_ORDER);
        final IntegralConfig config = integralConfigService.getOne(Wrappers.lambdaQuery(new IntegralConfig()).eq(IntegralConfig::getName, "积分规则"));
        final BigDecimal amount = BigDecimal.valueOf(withdrawAmount).divide(BigDecimal.valueOf(config.getIntegral()));
        financeManagementService.save(new FinanceManagement().setWithdrawIntegral(Long.valueOf(withdrawAmount))
                .setWithdrawMoney(amount)
                .setWithdrawType("微信")
                .setUserName(shop.getShopNumber())
                .setStatus("待审核")
                .setOrderNumber(withdrawOrderNo)
                .setBankName(shop.getBankName())
                .setBankNumber(shop.getBankNumber())
                .setBankUserName(shop.getBankUserName()));
        exchangeRecordService.save(new ExchangeRecord()
                .setOrderNo(withdrawOrderNo)
                .setType("提现")
                .setSource("门店")
                .setAmount(amount)
                .setAccount(shop.getShopNumber()).setExchangeStatus("待审核"));
        return true;
    }

    @Override
    public AttractDataVO shopDataStatistics(Long shopId) {
        final List<ClientUserManagement> clients = clientUserManagementService.list(Wrappers.lambdaQuery(new ClientUserManagement())
                .eq(ClientUserManagement::getShopId, shopId));
        AttractDataVO dataVO = new AttractDataVO().setOrderCount(0).setAttractCount(0);
        if (CollUtil.isEmpty(clients)) {
            return dataVO;
        }
        final List<Long> clientIds = clients.stream().map(ClientUserManagement::getId).collect(Collectors.toList());
        final int recycleOrderCount = recycleOrderService.count(Wrappers.lambdaQuery(new RecycleOrder())
                .in(RecycleOrder::getClientId, clientIds));
        final int houseOrderCount = housekeepingOrderService.count(Wrappers.lambdaQuery(new HousekeepingOrder())
                .in(HousekeepingOrder::getClientId, clientIds));
        return dataVO.setAttractCount(clientIds.size()).setOrderCount(houseOrderCount + recycleOrderCount);
    }

    @Override
    public PayInfoVO payCompute(String couponNo, BigDecimal amount) {
        final PayInfoVO infoVO = new PayInfoVO();
        final ClientCoupon record = clientCouponService.getOne(Wrappers.lambdaQuery(new ClientCoupon()).eq(ClientCoupon::getCouponNo, couponNo));
        final ClientUserManagement client = userManagementService.getById(record.getClientId());
        final Long integral = client.getIntegral();
        final IntegralConfig config = integralConfigService.getOne(Wrappers.lambdaQuery(new IntegralConfig()).eq(IntegralConfig::getType, "1"));
        final Long configIntegral = config.getIntegral();
        final CouponStock stock = stockService.getById(couponNo);
        final Coupon coupon = couponService.getById(stock.getCouponId());
        Assert.isTrue(StrUtil.equals(coupon.getStatus(), "已生效"), () -> new BusinessException("该优惠券未生效或失效"));
        Assert.isTrue(StrUtil.equals(record.getStatus(), "未使用"), () -> new BusinessException("该优惠券已使用"));
        String pattern = "[^0-9]";
        if (StrUtil.equals(coupon.getType(), "满减券")) {
            final String condition = coupon.getConditionDetail();
            final Pattern compile = Pattern.compile(pattern);
            final Matcher matcher = compile.matcher(condition);
            final String conditionReduction = matcher.replaceAll("").trim();
            final BigDecimal conditionReductionNum = (BigDecimal) NumberUtil.parseNumber(conditionReduction);
            Assert.isTrue(amount.compareTo(conditionReductionNum) >= 0, () -> new BusinessException("该满减券不符合使用条件,请选择其他优惠券"));
        }
        final BigDecimal denomination = coupon.getDenomination();
        final BigDecimal computeResult = amount.subtract(denomination);
        final BigDecimal availableBalance = BigDecimal.valueOf(integral)
                .divide(BigDecimal.valueOf(configIntegral), 2, RoundingMode.HALF_UP);
        if (computeResult.compareTo(BigDecimal.ZERO) <= 0) {
            infoVO.setAmount(BigDecimal.ZERO);
        } else {
            final BigDecimal actualAmount = computeResult.subtract(availableBalance);
            if (actualAmount.compareTo(BigDecimal.ZERO) <= 0) {
                infoVO.setAmount(BigDecimal.ZERO);
            } else {
                infoVO.setAmount(actualAmount);
            }

        }

        return infoVO.setBlueCoin(integral)
                .setCouponDenomination(denomination)
                .setCouponName(coupon.getCouponName())
                .setCouponNo(couponNo)
                .setOriginAmount(amount);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean blueCoinPay(NativePayDTO payDTO) {
        final String couponNo = payDTO.getCouponNo();
        final CouponStock couponStock = stockService.getById(couponNo);
        final ClientCoupon record = clientCouponService.getOne(Wrappers.lambdaQuery(new ClientCoupon()).eq(ClientCoupon::getCouponNo, payDTO.getCouponNo()));
        final Coupon coupon = couponService.getById(couponStock.getCouponId());
        final IntegralConfig config = integralConfigService.getOne(Wrappers.lambdaQuery(new IntegralConfig()).eq(IntegralConfig::getType, "1"));
        final String shopChargeOrderNo = orderNoGenerateUtil.getIncr(OrderInfoConstant.SHOP_CHARGE_NO);
        final ClientUserManagement user = userManagementService.getById(record.getClientId());
        final ShopManagement shop = shopManagementService.getById(payDTO.getShopId());
        final BigDecimal amount = Objects.isNull(payDTO.getAmount()) ? BigDecimal.ZERO : payDTO.getAmount();
        final ShopExchangeRecord exchangeRecord = new ShopExchangeRecord()
                .setAmount(amount)
                .setOriginAmount(payDTO.getOriginAmount())
                .setCouponNo(couponNo)
                .setDiscount(coupon.getDenomination())
                .setCouponDetail(coupon.getConditionDetail())
                .setUserId(user.getId())
                .setOrderNo(shopChargeOrderNo)
                .setShopId(payDTO.getShopId())
                .setShopName(shop.getShopName())
                .setBlueCoinPay(payDTO.getBlueCoin() == null ? 0L : payDTO.getBlueCoin());
        exchangeRecordMapper.insert(exchangeRecord.setPayType("积分支付").setIncomeType("用户下单"));
        final BigDecimal chargeBlueCoins = amount.multiply(BigDecimal.valueOf(config.getIntegral()), MathContext.UNLIMITED);
        final BigDecimal totalBlueCoins = BigDecimal.valueOf(payDTO.getBlueCoin()).add(chargeBlueCoins);
        userManagementService.updateById(user.setIntegral(user.getIntegral() - payDTO.getBlueCoin()));
        billRecordService.save(new BillRecord().setAccount(shop.getShopNumber())
                .setIncome(totalBlueCoins)
                .setOrderNo(shopChargeOrderNo)
                .setType(1).setUserType(3));
        clientCouponService.updateById(record.setStatus("已使用"));
        shopManagementService.updateById(shop.setIntegral(shop.getIntegral() + totalBlueCoins.longValue()));
        return true;
    }

    @Override
    public Map<String, String> payCallBack(HttpServletRequest request) {
        final Map<String, String> callBack = wechatPaymentService.payCallBack(request);
        final String code = callBack.get("code");
        final String outTradeNo = callBack.get("outTradeNo");
        final ExchangeRecord record = exchangeRecordService.getById(outTradeNo);
        final ShopExchangeRecord shopExchangeRecord = exchangeRecordMapper.selectById(record.getOrderNo());
        if (!StrUtil.equals(code, "500")) {
            if (!StrUtil.equals(record.getExchangeStatus(), "已充值")) {
                updateInfoByCode(record, shopExchangeRecord);
                shopExchangeRecord.setStatus("支付失败");
            } else {
                shopExchangeRecord.setStatus("支付成功");
            }
        } else {
            updateInfoByCode(record, shopExchangeRecord);
            shopExchangeRecord.setStatus("支付失败");
        }
        exchangeRecordMapper.updateById(shopExchangeRecord);
        return callBack;
    }

    private void updateInfoByCode(ExchangeRecord record, ShopExchangeRecord shopExchangeRecord) {
        final String clientId = record.getAccount();
        final Long blueCoin = shopExchangeRecord.getBlueCoinPay();
        if (blueCoin == 0 || Objects.isNull(blueCoin)) {
            return;
        }
        final ClientUserManagement client = userManagementService.getById(Long.valueOf(clientId));
        userManagementService.updateById(client.setIntegral(client.getIntegral() + blueCoin));
        final ShopManagement shop = shopManagementService.getById(shopExchangeRecord.getShopId());
        shopManagementService.updateById(shop.setIntegral(shop.getIntegral() - blueCoin));
        final CouponStock stock = stockService.getOne(Wrappers.lambdaQuery(new CouponStock())
                .eq(CouponStock::getCouponNo, shopExchangeRecord.getCouponNo()));
        stockService.updateById(stock.setStatus("未使用"));
    }

    @Override
    public List<AttractDataVO> shopDataStatistics(AttractDataQueryDTO dataQueryDTO) {
        final Long shopId = dataQueryDTO.getShopId();
        ClientUserManagement client = new ClientUserManagement();
        final Date begin = dataQueryDTO.getSearchTimeBegin();
        final Date end = dataQueryDTO.getSearchTimeEnd();
        final ClientUserManagementMapper clientMapper = (ClientUserManagementMapper) clientUserManagementService.getBaseMapper();
        final BillRecordMapper billMapper = (BillRecordMapper) billRecordService.getBaseMapper();
        final BillRecord billRecord = new BillRecord();
        final RecycleOrder recycleOrder = new RecycleOrder();
        final HousekeepingOrder housekeepingOrder = new HousekeepingOrder();
        final Boolean isMonth = dataQueryDTO.getIsMonth();
        final LambdaQueryWrapper<BillRecord> billWrapper = Wrappers.lambdaQuery(billRecord);
        List<AttractDataVO> attractData = new ArrayList<>();
        LambdaQueryWrapper<ClientUserManagement> queryWrapper = Wrappers.lambdaQuery(client);
        if (Objects.nonNull(shopId)) {
            final ShopManagement shop = shopManagementService.getById(shopId);
            final Long salesmanId = shop.getSalesmanId();
            queryWrapper.eq(ClientUserManagement::getShopId, shopId);
            if (Objects.nonNull(begin)) {
                queryWrapper.ge(ClientUserManagement::getCreatedTime, begin);
            } else if (Objects.nonNull(end)) {
                queryWrapper.le(ClientUserManagement::getCreatedTime, end);
            }
            final List<ClientUserManagement> attractList = clientUserManagementService.list(queryWrapper);
            final List<Long> clientIds = attractList.stream().map(ClientUserManagement::getId).collect(Collectors.toList());
            if (isMonth) {
                attractData = clientMapper.shopAttractGroupByMonth(clientIds);
            } else {
                attractData = clientMapper.shopAttractGroupByDay(clientIds);
            }
            if (CollUtil.isNotEmpty(attractData)) {
                attractData.forEach(a -> {
                    final String date = a.getDate();
                    final int recycleCount = recycleOrderService.count(Wrappers.lambdaQuery(recycleOrder)
                            .in(RecycleOrder::getClientId, clientIds)
                            .like(RecycleOrder::getCreatedTime, date));
                    final int houseCount = housekeepingOrderService.count(Wrappers.lambdaQuery(housekeepingOrder)
                            .in(HousekeepingOrder::getClientId, clientIds)
                            .like(HousekeepingOrder::getCreatedTime, date));
                    initBillRecordWrapper(billWrapper, isMonth);
                    final BigDecimal shopSumPercentage = billMapper.sumPercentage(billWrapper.eq(BillRecord::getUserType, 3)
                            .eq(BillRecord::getUserId, shopId).like(BillRecord::getCreateTime, date));
                    billWrapper.clear();
                    initBillRecordWrapper(billWrapper, isMonth);
                    final BigDecimal salesmanSumPercentage = billMapper.sumPercentage(billWrapper.eq(BillRecord::getUserType, 4)
                            .eq(BillRecord::getUserId, salesmanId).like(BillRecord::getCreateTime, date));
                    billWrapper.clear();
                    BigDecimal recycleFinishedOrders = recycleOrderService.sumFinishedOrder(Wrappers.lambdaQuery(recycleOrder).in(RecycleOrder::getClientId, clientIds)
                            .like(RecycleOrder::getCreatedTime, date).isNotNull(RecycleOrder::getIntegral).eq(RecycleOrder::getStatus, "6"));
                    BigDecimal houseFinishedOrders = housekeepingOrderService.sumFinishedOrder(Wrappers.lambdaQuery(housekeepingOrder).in(HousekeepingOrder::getClientId, clientIds)
                            .like(HousekeepingOrder::getCreatedTime, date).eq(HousekeepingOrder::getStatus, "5"));
                    BigDecimal recycleSum = Objects.isNull(recycleFinishedOrders) ? BigDecimal.ZERO : recycleFinishedOrders;
                    BigDecimal houseSum = Objects.isNull(houseFinishedOrders) ? BigDecimal.ZERO : houseFinishedOrders;
                    final BigDecimal finishedSum = houseSum.add(recycleSum);
                    a.setOrderCount(houseCount + recycleCount)
                            .setShopDrawPercentage(shopSumPercentage)
                            .setSalesmanDrawPercentage(salesmanSumPercentage).setOrderSum(finishedSum);

                });
            }
        }
        return attractData;
    }

    private void initBillRecordWrapper(LambdaQueryWrapper<BillRecord> billWrapper, Boolean isMonth) {
        Integer[] types = {1, 2};
        billWrapper.in(BillRecord::getType, types);
        if (isMonth) {
            billWrapper.last("GROUP BY DATE_FORMAT(create_time,'%Y-%m')");
        } else {
            billWrapper.last("GROUP BY DATE_FORMAT(create_time,'%Y-%m-%d')");
        }
    }

}
